package com.bookseek.xreader.utils;

import com.bookseek.xreader.vo.FtsOriginTextVo;
import com.bookseek.xreader.vo.TraceTextVo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class WordTextUtils {


    public static  Pattern  CH_PA = Pattern.compile("[\u4e00-\u9fa5]");


    /**
     * 返回文本分割内容，标记高亮
     * @param text
     * @param patternMatchWordMap
     * @return
     */
    public static List<TraceTextVo> calcTraceHighLightSplitMerge(String text, Map<Pattern,String> patternMatchWordMap){
        if(JkhlUtils.isBlank(text) || patternMatchWordMap == null || patternMatchWordMap.size() == 0){
            return calcTraceHighLightSplitByPatternMatchWord(text,null,null);
        }
        List<TraceTextVo> colorTraceTextVos = calcColorTraceTextPosition(text,patternMatchWordMap);
        return mergeByColorTraceTexts(text,colorTraceTextVos);
    }

    /**
     * 从text中找出高亮文本的位置
     * @param text
     * @param patternMatchWordMap
     * @return
     */
    public static List<TraceTextVo> calcColorTraceTextPosition(String text,Map<Pattern,String> patternMatchWordMap){
        //无高亮文本时，返回空集合
        if(JkhlUtils.isBlank(text) || patternMatchWordMap == null || patternMatchWordMap.size() == 0){
            return new ArrayList<>(0);
        }
        Set<Pattern> patternSet = patternMatchWordMap.keySet();
        List<TraceTextVo> colorTraceTextVos = new ArrayList<>();
        for(Pattern pa : patternSet){
            if(pa == null){
                continue;
            }
            String matchWord = patternMatchWordMap.get(pa);
            if(JkhlUtils.isBlank(matchWord)){
                continue;
            }
            colorTraceTextVos.addAll(
                    calcTraceHighLightSplitByPatternMatchWord(text,pa,matchWord).stream().filter(d->d != null && d.showColor != null && d.showColor == 1).collect(Collectors.toList())
            );
        }
        return colorTraceTextVos;
    }
    /**
     * 多个关键词可能有重叠，需要合并
     * @param text
     * @param matchTexts 为原始匹配的文本
     * @return
     */
    public static List<TraceTextVo> calcHighLightSplitMerge(String text, List<String> matchTexts){
        if(JkhlUtils.isBlank(text) || matchTexts == null || matchTexts.size() == 0 || matchTexts.stream().noneMatch(JkhlUtils::notBlank)){
            return calcHighLightSplit(text,"");
        }
        List<TraceTextVo> allTraceTexts = matchTexts.stream().filter(d->JkhlUtils.notBlank(d)).map(d->JkhlUtils.toStr(d)).flatMap(d->calcHighLightSplit(text,d).stream())
                .collect(Collectors.toList());
        //合并高亮显示
        List<TraceTextVo> colorTraceTexts = allTraceTexts.stream().filter(d->d.showColor != null && d.showColor == 1).collect(Collectors.toList());
        return mergeByColorTraceTexts(text,colorTraceTexts);
    }
    public static List<TraceTextVo> calcHighLightSplit(String text,String matchText){
        List<TraceTextVo> datas = new ArrayList<>();
        if(JkhlUtils.isBlank(text) || JkhlUtils.isBlank(matchText)){
            TraceTextVo textVo = new TraceTextVo();
            textVo.text = text;
            textVo.startIndex = 0;
            textVo.endIndex = JkhlUtils.toStr(text).length();
            datas.add(textVo);
            return datas;
        }
        Pattern replacePa = normalKeywordToPattern(matchText);
        return calcHighLightSplitByPattern(text,replacePa);
    }


    public static Pattern normalKeywordToPattern(String normalKeyword){
        if(JkhlUtils.isBlank(normalKeyword)){
            return null;
        }
        return Pattern.compile(JkhlUtils.toStr(normalKeyword).replaceAll("[.*+?^${}()|\\[\\]]",".{1}"));
    }

    /**
     * 计算溯源高亮，当pattern 捕获到 matchWord 才高亮
     * @param text
     * @param pattern
     * @param matchWord
     * @return
     */
    public static List<TraceTextVo> calcTraceHighLightSplitByPatternMatchWord(String text,Pattern pattern,String matchWord ){
        List<TraceTextVo> datas = new ArrayList<>();
        if(JkhlUtils.isBlank(text) || pattern == null){
            TraceTextVo textVo = new TraceTextVo();
            textVo.text = text;
            textVo.startIndex = 0;
            textVo.endIndex = JkhlUtils.toStr(text).length();
            datas.add(textVo);
            return datas;
        }
        Matcher matcher = pattern.matcher(text);
        int startIndex = 0;
        while (matcher.find()){
            int matchStart = matcher.start();
            int matchEnd = matcher.end();
            String catchText = text.substring(matchStart,matchEnd);
            if(startIndex != matchStart){
                //需要将未匹配的添加
                TraceTextVo noMatchVo = new TraceTextVo();
                noMatchVo.text = text.substring(startIndex,matchStart);
                noMatchVo.showColor = 0;
                noMatchVo.startIndex = startIndex;
                noMatchVo.endIndex = matchStart;
                datas.add(noMatchVo);
            }
            //捕获到的文本包含 matchWord ，高亮显示
            //1、先进行contains粗选
            //2、在从 catchText里定位matchWord准确位置
            int matchWordIndex = catchText.indexOf(matchWord);
            if(matchWordIndex >= 0 ){
                //对catchText 再分割
                int matchWordStartOffset = matchStart+matchWordIndex;
                int matchWordEndOffset = matchWordStartOffset+matchWord.length();
                if(matchWordStartOffset != matchStart){
                    TraceTextVo matchVoA = new TraceTextVo();
                    matchVoA.showColor = 0;
                    matchVoA.startIndex = matchStart;
                    matchVoA.endIndex = matchWordStartOffset;
                    matchVoA.text = text.substring(matchVoA.startIndex,matchVoA.endIndex);
                    datas.add(matchVoA);
                }
                //精确位置
                TraceTextVo matchVo = new TraceTextVo();
                matchVo.showColor = 1;
                matchVo.startIndex = matchWordStartOffset;
                matchVo.endIndex = matchWordEndOffset;
                matchVo.text = text.substring(matchVo.startIndex,matchVo.endIndex);;
                datas.add(matchVo);

                if(matchWordEndOffset != matchEnd){
                    TraceTextVo matchVoB = new TraceTextVo();
                    matchVoB.showColor = 0;
                    matchVoB.startIndex = matchWordEndOffset;
                    matchVoB.endIndex = matchEnd;
                    matchVoB.text = text.substring(matchVoB.startIndex,matchVoB.endIndex);
                    datas.add(matchVoB);
                }
                //正则匹配移动的位置
                startIndex = matchEnd;
            }
        }
        if(startIndex != text.length()){
            //需要将未匹配的添加
            TraceTextVo noMatchVo = new TraceTextVo();
            noMatchVo.text = text.substring(startIndex,text.length());
            noMatchVo.showColor = 0;
            noMatchVo.startIndex = startIndex;
            noMatchVo.endIndex = text.length();
            datas.add(noMatchVo);
        }
        return datas;
    }
    public static List<TraceTextVo> calcHighLightSplitByPattern(String text,Pattern pattern ){
        List<TraceTextVo> datas = new ArrayList<>();
        if(JkhlUtils.isBlank(text) || pattern == null){
            TraceTextVo textVo = new TraceTextVo();
            textVo.text = text;
            textVo.startIndex = 0;
            textVo.endIndex = JkhlUtils.toStr(text).length();
            textVo.showColor = 0;
            datas.add(textVo);
            return datas;
        }

        Matcher matcher = pattern.matcher(text);
        int startIndex = 0;
        while (matcher.find()){
            int matchStart = matcher.start();
            int matchEnd = matcher.end();
            String catchText = text.substring(matchStart,matchEnd);
            if(startIndex != matchStart){
                //需要将未匹配的添加
                TraceTextVo noMatchVo = new TraceTextVo();
                noMatchVo.text = text.substring(startIndex,matchStart);
                noMatchVo.showColor = 0;
                noMatchVo.startIndex = startIndex;
                noMatchVo.endIndex = matchStart;
                datas.add(noMatchVo);
            }
            TraceTextVo matchVo = new TraceTextVo();
            matchVo.text = catchText;
            matchVo.showColor = 1;
            matchVo.startIndex = matchStart;
            matchVo.endIndex = matchEnd;
            startIndex = matchEnd;
            datas.add(matchVo);
        }
        if(startIndex != text.length()){
            //需要将未匹配的添加
            TraceTextVo noMatchVo = new TraceTextVo();
            noMatchVo.text = text.substring(startIndex,text.length());
            noMatchVo.showColor = 0;
            noMatchVo.startIndex = startIndex;
            noMatchVo.endIndex = text.length();
            datas.add(noMatchVo);
        }
        return datas;
    }
    /**
     * 判断是否可以merge
     * @param textVo
     * @param todoMerge
     */
    private static boolean mergeTextVo(TraceTextVo textVo,TraceTextVo todoMerge){
        int endIndex = textVo.endIndex;
        int todoStartIndex = todoMerge.startIndex;
        int todoEndIndex = todoMerge.endIndex;
        //由于数据端点已经排过序，判断端点是否有交集，只需要判断  todoStartIndex <= endIndex 既可
        if(todoStartIndex <= endIndex){
            //可以合并
            textVo.endIndex = Math.max(todoEndIndex, endIndex);
            todoMerge.merged = 1; //合并过，将状态置为1
            return true;
        }
        return false;
    }
    public static List<TraceTextVo> mergeByColorTraceTexts(String text,List<TraceTextVo> colorTraceTexts){
        if(colorTraceTexts == null || colorTraceTexts.size() == 0){
            List<TraceTextVo> oneDatas = new ArrayList<>(1);
            TraceTextVo traceTextVo = new TraceTextVo();
            traceTextVo.showColor = 0;
            traceTextVo.text = text;
            traceTextVo.startIndex = 0;
            traceTextVo.endIndex = text.length();
            traceTextVo.merged = 0;
            oneDatas.add(traceTextVo);
            return oneDatas;
        }
        colorTraceTexts = colorTraceTexts.stream().sorted(Comparator.comparing(TraceTextVo::getStartIndex).thenComparing(TraceTextVo::getEndIndex)).collect(Collectors.toList());
        //高亮文本合并
        List<TraceTextVo> mergeDatas = new ArrayList<>();
        int size = colorTraceTexts.size();
        for(int i = 0;i<size;i++){
            TraceTextVo textVo = colorTraceTexts.get(i);
            if(textVo.merged != null && textVo.merged == 1){
                continue;
            }
            //向后merge
            for(int j = i+1;j<size;j++){
                TraceTextVo todoMerge = colorTraceTexts.get(j);
                boolean mergeFlag = mergeTextVo(textVo,todoMerge);
                //数据端点已经排序过,如果 mergeFlag = false,可以直接break
                if(!mergeFlag){
                    break;
                }
            }
            mergeDatas.add(textVo);
            textVo.merged = 1;
        }
        List<TraceTextVo> results = new ArrayList<>();
        //根据 mergeDatas 将 text 重新切割
        int mergeSize = mergeDatas.size();
        //起始端点
        int firstMergeStartIndex = mergeDatas.get(0).startIndex;
        if(firstMergeStartIndex > 0 ){
            TraceTextVo noMatchTextVo = new TraceTextVo();
            noMatchTextVo.showColor = 0;
            noMatchTextVo.startIndex = 0;
            noMatchTextVo.endIndex = firstMergeStartIndex;
            noMatchTextVo.text = text.substring(noMatchTextVo.startIndex,noMatchTextVo.endIndex);
            results.add(noMatchTextVo);
        }
        for(int i = 0;i<mergeSize;i++){
            TraceTextVo curMerge = mergeDatas.get(i);
            curMerge.text = text.substring(curMerge.startIndex,curMerge.endIndex);
            int curEndIndex = curMerge.endIndex;
            int nextIndex = i+1;
            results.add(curMerge);
            if(nextIndex < mergeSize){
                TraceTextVo noMatchTextVo = new TraceTextVo();
                noMatchTextVo.showColor = 0;
                noMatchTextVo.startIndex = curEndIndex;
                noMatchTextVo.endIndex = mergeDatas.get(nextIndex).startIndex;
                noMatchTextVo.text = text.substring(noMatchTextVo.startIndex,noMatchTextVo.endIndex);
                results.add(noMatchTextVo);
            }
        }
        //末尾端点
        if(mergeDatas.get(mergeSize-1).endIndex < text.length()){
            TraceTextVo noMatchTextVo = new TraceTextVo();
            noMatchTextVo.showColor = 0;
            noMatchTextVo.startIndex = mergeDatas.get(mergeSize-1).endIndex;
            noMatchTextVo.endIndex = text.length();
            noMatchTextVo.text = text.substring(noMatchTextVo.startIndex,noMatchTextVo.endIndex);
            results.add(noMatchTextVo);
        }
        return results;
    }

    public static FtsOriginTextVo toFtsOriginStr(String dbStr){
        FtsOriginTextVo result = new FtsOriginTextVo();
        result.lines = new ArrayList<>();
        //霜 馀 已失 长淮 阔 。 | 空听 潺潺 清颍咽 。 | 佳人 犹唱 醉翁 词 ， <b>四十</b> 十三 三年 四十三 十三年 四十三年 <b>如电抹</b> 。 | 草头 秋露 流珠 滑 。 | 三五 盈盈 还 二八 。 | 与 余同 是 识 翁 人 ， 惟有 西湖 波底 月 。
        String[] strs = dbStr.split("[|]");
        Pattern colorPa = Pattern.compile("[<]b[>](?<htext>.*?)[<]/b[>]");
        Set<String> colorTexts = new HashSet<>(JkhlUtils.extractResultListByGroupNames(dbStr,colorPa,"htext"));
        result.colorWords = colorTexts;
        for(String text : strs){
            String[] splitTexts = text.split("\\s+");
            int len = splitTexts.length;
            List<String> sortTexts = new ArrayList<>();
            for(int j = len-1;j>=0;j--){
                String ch = splitTexts[j].replaceAll("[<]/?b[>]","");
                if(JkhlUtils.isBlank(ch)){
                    continue;
                }
                //非中文
                if(ch.length() == 1 || !JkhlUtils.match(CH_PA,ch)){
                    sortTexts.add(ch);
                }else{
                    if(!sortTexts.stream().anyMatch(d->d.contains(ch))){
                        sortTexts.add(ch);
                    }
                }
            }
            Collections.reverse(sortTexts);
            String str = String.join("",sortTexts);
            result.lines.add(str);
        }
        return result;
    }
    public static  List<List<TraceTextVo>> toFtsOriginSentence(String dbStr){
        List<List<TraceTextVo>> datas = new ArrayList<>();
        FtsOriginTextVo textVo = toFtsOriginStr(dbStr);
        if(textVo.lines != null && textVo.lines.size() > 0 ){
            List<String> words = new ArrayList<>(textVo.colorWords);
            textVo.lines.forEach(d->{
                datas.add(calcHighLightSplitMerge(d,words));
            });
        }
        return datas;
    }

    public static List<TraceTextVo> findMatchLine(String dbStr){
        return findMatchLineWithLen(dbStr,null);
    }
    public static List<TraceTextVo> findMatchLineWithLen(String dbStr,Integer maxLen){
        List<List<TraceTextVo>> allDatas = toFtsOriginSentence(dbStr);
        for(int i = 0;i<allDatas.size();i++){
            List<TraceTextVo> lines = allDatas.get(i);
            boolean flag = lines.stream().anyMatch(d->d.showColor != null && d.showColor == 1);
            if(flag){
                return lines;
            }
        }
        if(maxLen != null && maxLen > 0){
            List<TraceTextVo> allTextLines = allDatas.stream().flatMap(d->d.stream()).collect(Collectors.toList());
            List<TraceTextVo> targetDatas = new ArrayList<>();
            Integer calcLen = 0;
            for(TraceTextVo textVo : allTextLines){
                targetDatas.add(textVo);
                Integer textLen = JkhlUtils.toStr(textVo.text).length();
                calcLen += textLen;
                if(calcLen > maxLen){
                    if(targetDatas.size() >= 2){
                        targetDatas.remove(targetDatas.size()-1);
                    }
                    break;
                }
            }
            return targetDatas;
        }
        return allDatas.size() > 0 ? allDatas.get(0) : new ArrayList<>();
    }
}
